package lpca.model 
{	
	import lpca.utils.MazeGenerator.*;
	import lpca.utils.Cell;
	import lpca.utils.IMaze;
	import lpca.utils.Maze;	
	import lpca.utils.Point;
	import lpca.utils.Trace;
	import lpca.utils.Wave;
	import lpca.view.ViewCell;
	import lpca.view.ViewMaze;	
	import lpca.utils.IWave;
	import mx.controls.Label;
	
	/**
	 * @author d.shakshin@gmail.com
	 */
	public class LpcaModel 
	{			
		private var maze: IMaze;
		private var wave: IWave;	
		private var view: ViewMaze;				
		private var _stackChange: Vector.<Point>;
		private var track: Vector.<Point>;
				
		public var stag: int; 
		public var step: int;  
				
		public static const visible: int = 1;
		public static const visibleValueWave: int = 2;
		public static const checked: int = 3;
		public static const both: int = 0;
		
		public static const stageWave: int = 1;
		public static const stageTrace: int = 2;
			
		public function getCountStep(): int
		{			
			return wave.getCountWave();			
		}
			
		public function getSizeY(): int
		{
			return maze.getSizeY();
		}
		
		public function getSizeX(): int
		{
			return maze.getSizeX();
		}
				
		public function setCell(point: Point, cell: Cell): void
		{								
			if ((cell == null) || (point == null))
				return;									
			maze.setCell(new Point(point.x, point.y), cell);						
			if (cell.view !== null)			
				cell.view.update();			
			_stackChange.push(point);			
		}
		
		public function getCell(point: Point): Cell
		{						
			return maze.getCell(point);			
		}
					
		//бизнес-логика	
		public function checkWave(step: int): Boolean
		{
			trace("Проверка волны");
			var userVector: Vector.<Point> = this.stackChange;
			if (step == this.getCountStep())
			{				
				if (userVector.length !== 1)
					return false;
				return userVector.pop().isEqual(maze.getFinishPoint());		
			}
			var correctVector: Vector.<Point> = wave.getWavePoints(step - 1);									
			if (userVector.length == 0)
				return false;						
			var errorVector: Vector.<Point> = Point.compareVector(userVector, correctVector);
			correctVector.splice(0, correctVector.length);
			return (errorVector.length == 0)//если вектор ошибок пустой, то ошибок нету			
		}
		
		public function checkTrace(step: int): Boolean
		{
			trace("Проверка маршрута");						
			var userVector: Vector.<Point> = new Vector.<Point>;						
			if (stackChange.length !== 1)
				return false;
			userVector.push(this.stackChange.pop());			
			var curPoint: Point = userVector.pop();			
			if (step == 1) //1-ый шаг конечная точка
				if (curPoint.isEqual( this.maze.getFinishPoint() ))							
						return true;					
				else				
				{
					userVector.pop();
					return false;									
				}									
			//елси значение текущей ячейки на 1 меньше значения предыдущий ячейки			
			if ((wave.getlevelWave(getPointOracleTrace(step - 1)) - wave.getlevelWave(curPoint)) == 1)
				return true;
			return false;
		}
		
		public function oracleWave(step: int): void
		{			
			this._stackChange = this.getVectorOracleWave(step);
				
		}
		
		public function getPointOracleTrace(step: int): Point
		{
			if (step < this.track.length)
				return this.track[step - 1];
			return this.getStartPoint();
		}
		
		public function getVectorOracleWave(step: int): Vector.<Point>
		{
			var returnVector: Vector.<Point> = new Vector.<Point>;
			if (step < this.getCountStep())
				returnVector = wave.getWavePoints(step - 1)
			else
				returnVector.push(this.getFinishPoint());			
			return returnVector;
		}
		
		public function oracleTrace(step: int): void
		{			
			this._stackChange.push(this.getPointOracleTrace(step));			
		}
			
		
		public function setAllCells(selector: Vector.<int>, visible: Boolean = false, 
										visibleValueWave: Boolean = false, checked: Boolean = false): void
		{
			for (var y: int = 1; y <= maze.getSizeY(); y++)
				for (var x: int = 1; x <= maze.getSizeX(); x++)
				{					
					var point: Point = new Point(x, y);
					var cell: Cell = maze.getCell(new Point(x, y));
					for each(var iterator: int in selector)
					{
						
						switch (iterator)
						{									
							case LpcaModel.checked: cell.checked = checked;
								break;
							case LpcaModel.visible: cell.visible = visible; 
								break;
							case LpcaModel.visibleValueWave: cell.visibleValueWave = visibleValueWave;
								break;
						}										
					}
					this.setCell(point, cell); 
					cell.view.update();
				}										
		}
				
		/*
		public function setVectorPoint(vector: Vector.<Point>, selector: int = both, visible: Boolean = false, 
										checked: Boolean = false, visibleValueWave: Boolean = false): void
		{
			if (vector == null)
				return;
			for each(var point: Point in vector)
			{											
				var cell: Cell = maze.getCell(point);				
				switch(selector)
				{
					case LpcaModel.checked: cell.checked = checked; 
						break;
					case LpcaModel.visible: cell.visible = visible; 
						break;
					case LpcaModel.visibleValueWave: cell.visibleValueWave = visibleValueWave;
					default:
						{ 
							cell.visible = visible;
							cell.checked = checked;
							cell.visibleValueWave = visibleValueWave;
						}
				}							
				maze.setCell(point, cell); //FIXME: отладка
				cell.view.update();
			}			
		}
		*/
		public function setVectorPoint(vector: Vector.<Point>, selector: Vector.<int>, visible: Boolean = false, 
										visibleValueWave: Boolean = false, checked: Boolean = false)
		{
			for each(var point: Point in vector)
			{									
				var cell: Cell = maze.getCell(point);
				for each(var iterator: int in selector)
				{					
					switch (iterator)
					{									
						case LpcaModel.checked: cell.checked = checked;
							break;
						case LpcaModel.visible: cell.visible = visible; 
							break;
						case LpcaModel.visibleValueWave: cell.visibleValueWave = visibleValueWave;
							break;
					}									
				}
				this.setCell(point, cell); 
				cell.view.update();
			}
		}		
		
		//FIXME:  вынести в класс Point
		public function getVectorPointWithBorder(vector: Vector.<Point>): Vector.<Point>
		{
			var border: Vector.<Point> = new Vector.<Point>;
			for each(var point: Point in vector)
			{
				for (var direction: int = maze.getMinDirection(); direction <= maze.getMaxDirection(); direction++)
				{
					if (maze.getNearestCell(point, direction) !== null)
					{
						var point: Point = point.next(direction);
						if (!Point.searchPointInVector(border, point) && !Point.searchPointInVector(vector, point))
							border.push(point);
					}
				}
			}
			for each(var bufferPoint: Point in vector)
			{
				border.push(bufferPoint);
			}			
			return border;			
		}
		
		public function getStartPoint(): Point
		{
			return maze.getStartPoint();
		}
		
		public function getFinishPoint(): Point
		{
			return maze.getFinishPoint();
		}
			
		public function get stackChange(): Vector.<Point>
		{
			return this._stackChange.slice(0, _stackChange.length);
		}
		
		public function clearStackChange(): void
		{
			this._stackChange.splice(0, _stackChange.length);
		}
		
		public function LpcaModel(width: int = 20, height: int = 20,  complexity: int = 50) 
		{
			this._stackChange = new Vector.<Point>;			
			this.maze = new Maze(width, height);									
			//генерация лабиринта
			var mazegen: MazeGeneratorPattern = new MazeGeneratorPattern();
            mazegen.generateMaze(this.maze, new MazeGeneratorSet());
			//выполнение алгоритма
			this.wave = new Wave(this.maze, maze.getStartPoint(), complexity);							
			this.track = wave.getTrace().getVector();
			//вывод отладочной информации
			this.maze.show();
			this.wave.show();			
			var startCell: Cell = this.maze.getCell(this.maze.getStartPoint());
			startCell.visible = true;
			this.maze.setCell(this.maze.getStartPoint(), startCell);			
		}			
	}		
}